# frozen_string_literal: true

require_relative '../unit_helper'

module Watir
  module Locators
    class TextField
      describe SelectorBuilder do
        include LocatorSpecHelper

        let(:selector_builder) { described_class.new(attributes, query_scope) }
        let(:uppercase) { 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ' }
        let(:lowercase) { 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ' }
        let(:negative_types) do
          "translate(@type,'#{uppercase}','#{lowercase}')!=translate('file','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('radio','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('checkbox','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('submit','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('reset','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('image','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('button','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('hidden','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('range','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('color','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('date','#{uppercase}','#{lowercase}') and " \
            "translate(@type,'#{uppercase}','#{lowercase}')!=translate('datetime-local','#{uppercase}','#{lowercase}')"
        end

        describe '#build' do
          it 'without any arguments' do
            selector = {}
            built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]"}

            expect(selector_builder.build(selector)).to eq built
          end

          context 'with type' do
            it 'specified text field type that is text' do
              selector = {type: 'text'}
              built = {xpath: ".//*[local-name()='input']" \
                              "[translate(@type,'#{uppercase}','#{lowercase}')=" \
                              "translate('text','#{uppercase}','#{lowercase}')]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'specified text field type that is not text' do
              selector = {type: 'number'}
              built = {xpath: ".//*[local-name()='input']" \
                              "[translate(@type,'#{uppercase}','#{lowercase}')=" \
                              "translate('number','#{uppercase}','#{lowercase}')]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'true locates text field with a type specified' do
              selector = {type: true}
              built = {xpath: ".//*[local-name()='input'][#{negative_types}]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'false locates text field without type specified' do
              selector = {type: false}
              built = {xpath: ".//*[local-name()='input'][not(@type)]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'raises exception when a non-text field type input is specified' do
              selector = {type: 'checkbox'}
              msg = 'TextField Elements can not be located by type: checkbox'

              expect { selector_builder.build(selector) }
                .to raise_exception Watir::Exception::LocatorException, msg
            end
          end

          context 'with index' do
            it 'positive' do
              selector = {index: 4}
              built = {xpath: "(.//*[local-name()='input'][not(@type) or (#{negative_types})])[5]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'negative' do
              selector = {index: -3}
              built = {xpath: "(.//*[local-name()='input'][not(@type) or (#{negative_types})])[last()-2]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'last' do
              selector = {index: -1}
              built = {xpath: "(.//*[local-name()='input'][not(@type) or (#{negative_types})])[last()]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'does not return index if it is zero' do
              selector = {index: 0}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'raises exception when index is not an Integer' do
              selector = {index: 'foo'}
              msg = /expected one of \[(Integer|Fixnum)\], got "foo":String/

              expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
            end
          end

          context 'with wrong tag name' do
            # TODO: Should this throw exception?
            it 'ignores tag name' do
              selector = {tag_name: 'div'}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]"}

              expect(selector_builder.build(selector)).to eq built
            end
          end

          context 'with text' do
            it 'String for value' do
              selector = {text: 'Developer'}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]", text: 'Developer'}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'Simple Regexp for value' do
              selector = {text: /Dev/}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]", text: /Dev/}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'returns complicated Regexp to the locator as a value' do
              selector = {text: /^foo$/}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]", text: /^foo$/}

              expect(selector_builder.build(selector)).to eq built
            end
          end

          context 'with label' do
            it 'using String' do
              selector = {label: 'First name'}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
                              "[@id=//label[normalize-space()='First name']" \
                              "/@for or parent::label[normalize-space()='First name']]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'uses String with hidden text' do
              selector = {label: 'With hidden text'}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
                              "[@id=//label[normalize-space()='With hidden text']" \
                              "/@for or parent::label[normalize-space()='With hidden text']]"}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'using simple Regexp' do
              selector = {label: /First/}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
                              '[@id=//label[contains(normalize-space(), "First")]' \
                              '/@for or parent::label[contains(normalize-space(), "First")]]'}

              expect(selector_builder.build(selector)).to eq built
            end

            it 'using complex Regexp' do
              selector = {label: /([qa])st? name/}
              built = {xpath: ".//*[local-name()='input'][not(@type) or (#{negative_types})]" \
                              '[@id=//label[contains(normalize-space(), "s") and ' \
                              'contains(normalize-space(), " name")]' \
                              '/@for or parent::label[contains(normalize-space(), "s") ' \
                              'and contains(normalize-space(), " name")]]',
                       label_element: /([qa])st? name/}

              expect(selector_builder.build(selector)).to eq built
            end
          end

          context 'with multiple locators' do
            it 'locates using tag name, class, attributes and text' do
              selector = {text: 'Developer', class: /c/, id: true}
              built = {xpath: ".//*[local-name()='input'][contains(@class, \"c\")]" \
                              "[not(@type) or (#{negative_types})][@id]", text: 'Developer'}

              expect(selector_builder.build(selector)).to eq built
            end
          end
        end
      end
    end
  end
end
